package com.ruoyi.flowable.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.flowable.domain.TaskVo;
import com.ruoyi.flowable.domain.bo.FormDataBo;
import com.ruoyi.flowable.domain.vo.FormDataVo;
import com.ruoyi.flowable.service.IFormDataService;
import com.ruoyi.flowable.service.ITaskService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.Process;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TaskServiceImpl implements ITaskService {

    @Resource
    protected TaskService taskService;

    @Resource
    private ISysRoleService sysRoleService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    protected RuntimeService runtimeService;

    @Resource
    protected IFormDataService formDataService;

    @Override
    public TableDataInfo todoList(Integer pageNum, Integer pageSize, String taskName) {
        TableDataInfo dataInfo = new TableDataInfo();
        String userName = LoginHelper.getUsername();
//        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles(); //暂时不支持角色审批，应该改为非部门的岗位

        TaskQuery taskQuery = taskService.createTaskQuery()
            .active()
            //.includeProcessVariables() //列表不需要变量
            .taskCandidateOrAssigned(userName);
        if (taskName != null && !taskName.isEmpty()) {
            taskQuery.taskNameLike(taskName);
        }
//        if (!roles.isEmpty()) {
//            List<String> roleIds = roles.stream().map(SysRole::getRoleId).map(x -> x + "").collect(Collectors.toList());
//            taskQuery.taskCandidateGroupIn(roleIds);
//        }
        dataInfo.setTotal(taskQuery.count());
        taskQuery.orderByTaskCreateTime().desc();
        List<Task> tasks = taskQuery.listPage(pageSize * (pageNum - 1), pageSize);
        if (tasks.isEmpty()) return dataInfo;

        //构造流程实例数据
        Set<String> instIds = tasks.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet());
        List<HistoricProcessInstance> insts = historyService.createHistoricProcessInstanceQuery().processInstanceIds(instIds).list();
        Map<String, String> instUserMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getStartUserId));
        Map<String, String> defNameMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getProcessDefinitionName));
        Map<String, String> deployIdMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getDeploymentId));

        List<TaskVo> list = new ArrayList<>();
        for (Task task : tasks) {
            TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
            taskVo.setTaskId(task.getId());
            taskVo.setTaskName(task.getName());

            taskVo.setProcessDefinition(defNameMap.get(task.getProcessInstanceId()));
            taskVo.setDeploymentId(deployIdMap.get(task.getProcessInstanceId()));

            String username = instUserMap.get(task.getProcessInstanceId());
            SysUser startUser = sysUserService.selectUserByUserName(username);
            taskVo.setStartUserName(username);
            taskVo.setStartNickName(startUser.getNickName());
            if (startUser.getDept()!=null) taskVo.setStartDeptName(startUser.getDept().getDeptName());
            list.add(taskVo);

        }
        dataInfo.setRows(list);

        return dataInfo;
    }

    @Override
    public TableDataInfo finishList(Integer pageNum, Integer pageSize, String taskName) {
        TableDataInfo dataInfo = new TableDataInfo();
        String userName = LoginHelper.getUsername();

        HistoricTaskInstanceQuery taskInstanceQuery = historyService.createHistoricTaskInstanceQuery().finished()
            .taskAssignee(userName).orderByHistoricTaskInstanceEndTime().desc();
        if (taskName != null && !taskName.isEmpty()) {
            taskInstanceQuery.taskNameLike(taskName);
        }

        dataInfo.setTotal(taskInstanceQuery.count());
        List<HistoricTaskInstance> taskInstanceList = taskInstanceQuery.listPage(pageSize * (pageNum - 1), pageSize);
        if (taskInstanceList.isEmpty()) return dataInfo;

        //构造流程实例数据
        Set<String> instIds = taskInstanceList.stream().map(HistoricTaskInstance::getProcessInstanceId).collect(Collectors.toSet());
        List<HistoricProcessInstance> insts = historyService.createHistoricProcessInstanceQuery().processInstanceIds(instIds).list();
        Map<String, String> instUserMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getStartUserId));
        Map<String, String> defNameMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getProcessDefinitionName));
        Map<String, String> deployIdMap = insts.stream().collect(Collectors.toMap(HistoricProcessInstance::getId, HistoricProcessInstance::getDeploymentId));

        List<TaskVo> list = new ArrayList<>();
        for (HistoricTaskInstance task : taskInstanceList) {
            TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
            taskVo.setTaskId(task.getId());
            taskVo.setTaskName(task.getName());

            taskVo.setProcessDefinition(defNameMap.get(task.getProcessInstanceId()));
            taskVo.setDeploymentId(deployIdMap.get(task.getProcessInstanceId()));
            taskVo.setDuration(DateUtils.getDate(task.getDurationInMillis()));

            String username = instUserMap.get(task.getProcessInstanceId());
            SysUser startUser = sysUserService.selectUserByUserName(username);
            taskVo.setStartUserName(username);
            taskVo.setStartNickName(startUser.getNickName());
            if (startUser.getDept()!=null) taskVo.setStartDeptName(startUser.getDept().getDeptName());
            list.add(taskVo);

        }
        dataInfo.setRows(list);

        return dataInfo;
    }

    @Override
    public TaskVo selectTaskByTaskId(String taskId) {
        Task task = taskService.createTaskQuery().active().includeProcessVariables().taskId(taskId).singleResult();
        if (task == null) {
            throw new ServiceException("任务不存在");
        }
        TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class);
        taskVo.setProcessVariables(task.getProcessVariables());
        taskVo.setTaskId(task.getId());
        taskVo.setTaskName(task.getName());
        return taskVo;
    }

    @Override
    public Map<String, Object> taskRecord(String procInstId) {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().includeProcessVariables().processInstanceId(procInstId).singleResult();
        if (processInstance == null) {
            throw new ServiceException("流程实例不存在");
        }
        Map<String, Object> processVariables = processInstance.getProcessVariables();
        Object rejectNode = processVariables.get("rejectNode");
        if (rejectNode == null) rejectNode = "";
        Object stopType = processVariables.get("stopType");
        if (stopType == null) stopType = "流程结束";

        Map<String, Object> map = new HashMap<>();
        List<HistoricActivityInstance> list = historyService
            .createNativeHistoricActivityInstanceQuery()
            .sql("SELECT * FROM ACT_HI_ACTINST WHERE PROC_INST_ID_ = #{procInstId} ORDER BY ID_ ASC ")
            .parameter("procInstId", procInstId)
            .list();
        List<TaskVo> hisFlowList = new ArrayList<>();
        Map<String, Object> elementMap= new HashMap<>();

        SysUser startUser = sysUserService.selectUserByUserName(processInstance.getStartUserId());

        for (HistoricActivityInstance histInst : list) {
            if (StringUtils.isNotBlank(histInst.getTaskId()) && "userTask".equals(histInst.getActivityType())) {
                TaskVo taskVo = new TaskVo();
                taskVo.setTaskId(histInst.getTaskId());
                taskVo.setTaskName(histInst.getActivityName());
                taskVo.setCreateTime(histInst.getStartTime());
                taskVo.setEndTime(histInst.getEndTime());
                if (StringUtils.isNotBlank(histInst.getAssignee())) {
                    SysUser sysUser = sysUserService.selectUserByUserName(histInst.getAssignee());
                    if (sysUser != null){
                        taskVo.setAssignee(sysUser.getUserName());
                        taskVo.setAssigneeName(sysUser.getNickName());
                        taskVo.setAssigneeDeptName(sysUser.getDept().getDeptName());
                    }
                }
                // 展示审批人员
                List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(histInst.getTaskId());
                StringBuilder stringBuilder = new StringBuilder();
                for (HistoricIdentityLink identityLink : linksForTask) {
                    // 获选人,候选组/角色(多个)
                    if ("candidate".equals(identityLink.getType())) {
                        if (StringUtils.isNotBlank(identityLink.getUserId())) {
                            SysUser sysUser = sysUserService.selectUserByUserName(identityLink.getUserId());
                            stringBuilder.append(sysUser.getNickName()).append(",");
                        }
//                        if (StringUtils.isNotBlank(identityLink.getGroupId())) {
//                            SysRole sysRole = sysRoleService.selectRoleById(Long.parseLong(identityLink.getGroupId()));
//                            stringBuilder.append(sysRole.getRoleName()).append(",");
//                        }
                    }
                }
                if (StringUtils.isNotBlank(stringBuilder)) {
                    taskVo.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
                }

                taskVo.setDuration(histInst.getDurationInMillis() == null || histInst.getDurationInMillis() == 0 ? null : DateUtils.getDate(histInst.getDurationInMillis()));
                // 获取意见评论内容
                List<Comment> commentList = taskService.getTaskComments(histInst.getTaskId());
                commentList.forEach(comment -> {
                    if (histInst.getTaskId().equals(comment.getTaskId())) {
                        taskVo.setComment(comment.getFullMessage());
                    }
                });
                hisFlowList.add(taskVo);
            } else if ("startEvent".equals(histInst.getActivityType()) || "endEvent".equals(histInst.getActivityType())) {
                TaskVo taskVo = new TaskVo();
                taskVo.setEndTime(histInst.getEndTime());
                if ("startEvent".equals(histInst.getActivityType())) {
                    taskVo.setCreateTime(histInst.getStartTime());
                    taskVo.setAssignee(startUser.getUserName());
                    taskVo.setAssigneeName(startUser.getNickName());
                    if (startUser.getDept()!=null) taskVo.setAssigneeDeptName(startUser.getDept().getDeptName());
                    taskVo.setTaskName("发起流程");
                } else {
                    taskVo.setTaskName(stopType.toString());
                }
                hisFlowList.add(taskVo);
            }
            if (rejectNode.equals(histInst.getActivityId())){
                elementMap.clear();
            }
            elementMap.put(histInst.getActivityId(), histInst.getEndTime() != null);
        }
        map.put("flowList", hisFlowList);
        map.put("elementList", elementMap);
        return map;
    }

    @Override
    @Transactional
    public void complete(TaskVo taskVo) {
        Task task = getTask(taskVo.getTaskId());
        try {
            if ("发起者修改".equals(taskVo.getComment())) {
                taskService.addComment(taskVo.getTaskId(), taskVo.getProcessInstanceId(), "comment", "发起者修改");
            } else {
                taskService.addComment(taskVo.getTaskId(), taskVo.getProcessInstanceId(), "comment", "同意；审批意见：" + taskVo.getComment());
            }
            //保存表单数据
            Map<String, Object> submitData = taskVo.getFormData();
            if (submitData != null) {
                //重置流程变量
                Map<String, Object> vars = task.getProcessVariables();
                if (vars != null) {
                    for (String field : vars.keySet()) {
                        if (submitData.containsKey(field)) {
                            vars.put(field, submitData.get(field));
                        }
                    }
                    taskService.setVariables(taskVo.getTaskId(), vars);
                }
            }
            taskService.complete(taskVo.getTaskId());

            if (submitData != null){
                FormDataVo formData = formDataService.selectFormDataByInstId(task.getProcessInstanceId());
                FormDataBo formDataBo = BeanUtil.toBean(formData, FormDataBo.class);
                formDataBo.setSubmitData(JsonUtils.toJsonString(submitData));
                formDataService.updateByBo(formDataBo);
                formDataService.asyncSaveData(formDataBo); //落地表
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(e.getMessage());
        }
    }

    @Override
    public void taskReject(TaskVo taskVo) {
        Task task = getTask(taskVo.getTaskId());
        if (task.getProcessVariables().containsKey("rejectNode")) {
            taskService.addComment(taskVo.getTaskId(), task.getProcessInstanceId(), "comment", "驳回；审批意见：" + taskVo.getComment());
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(task.getProcessInstanceId())
                .moveExecutionsToSingleActivityId(executionIds, (String) task.getProcessVariables().get("rejectNode")).changeState();

        } else {
            throw new ServiceException("未设置发起者修改节点");
        }
    }

    @Override
    public void stopProcess(TaskVo taskVo) {
        Task task = getTask(taskVo.getTaskId());
        Process process = repositoryService.getBpmnModel(task.getProcessDefinitionId()).getMainProcess();
        List<EndEvent> endNodes = process.findFlowElementsOfType(EndEvent.class, false);
        if (!endNodes.isEmpty()) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "comment", "不同意；审批意见：" + taskVo.getComment());
            // 获取当前流程最后一个节点
            String endId = endNodes.get(0).getId();
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(task.getProcessInstanceId()).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            taskService.setVariable(task.getId(),"stopType","已终止");
            try {
                // 变更流程为已结束状态
                runtimeService.createChangeActivityStateBuilder()
                    .processInstanceId(task.getProcessInstanceId())
                    .moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException(e.getMessage());
            }
        }
    }

    private Task getTask(String taskId){
        String userName = LoginHelper.getUsername();
        Task task = taskService.createTaskQuery().taskId(taskId).taskCandidateOrAssigned(userName).includeProcessVariables().singleResult();
        if (task == null) {
            throw new ServiceException("任务不存在");
        }
        return task;
    }
}
